#include <math.h>

// 引入TensorFlow Lite Micro库中的相关头文件
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/examples/hello_world/hello_world_model_data.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_log.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
#include "tensorflow/lite/schema/schema_generated.h"

// 定义一个宏，用于执行单元测试
TF_LITE_MICRO_TESTS_BEGIN

// 定义一个单元测试，用于加载模型并进行推理
TF_LITE_MICRO_TEST(LoadModelAndPerformInference) {
  // 定义输入值和期望的输出值
  float x = 0.0f;
  float y_true = sin(x);

  // 将模型映射到一个可用的数据结构中，这里不涉及任何复制或解析操作，是一个非常轻量级的操作
  const tflite::Model* model = ::tflite::GetModel(g_hello_world_model_data);
  if (model->version() != TFLITE_SCHEMA_VERSION) {
    MicroPrintf(
        "Model provided is schema version %d not equal "
        "to supported version %d.\n",
        model->version(), TFLITE_SCHEMA_VERSION);
  }

  // 获取所有操作的实现
  tflite::AllOpsResolver resolver;

  // 定义一个常量，表示张量数组的大小
  constexpr int kTensorArenaSize = 2000;
  // 定义一个张量数组
  uint8_t tensor_arena[kTensorArenaSize];

  // 构建一个解释器，用于运行模型
  tflite::MicroInterpreter interpreter(model, resolver, tensor_arena,
                                       kTensorArenaSize);
  // 为模型的张量分配内存
  TF_LITE_MICRO_EXPECT_EQ(interpreter.AllocateTensors(), kTfLiteOk);

  // 获取模型的输入张量的指针
  TfLiteTensor* input = interpreter.input(0);

  // 确保输入张量具有我们期望的属性
  TF_LITE_MICRO_EXPECT(input != nullptr);
  // 输入张量的"dims"属性告诉我们张量的维度。它有一个元素，每个维度都有一个元素。我们的输入是一个包含1个元素的2D张量，所以"dims"应该有2个元素。
  TF_LITE_MICRO_EXPECT_EQ(2, input->dims->size);
  // 输入张量的每个元素的长度告诉我们张量的长度。我们应该期望有两个单元素张量（其中一个包含在另一个中）。
  TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]);
  TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[1]);
  // 输入张量是一个8位整数值
  TF_LITE_MICRO_EXPECT_EQ(kTfLiteInt8, input->type);

  // 获取输入张量的量化参数
  float input_scale = input->params.scale;
  int input_zero_point = input->params.zero_point;

  // 将浮点数输入从整数量化到整数
  int8_t x_quantized = x / input_scale + input_zero_point;
  // 将量化后的输入放入模型的输入张量中
  input->data.int8[0] = x_quantized;

  // 运行模型并检查是否成功
  TfLiteStatus invoke_status = interpreter.Invoke();
  TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, invoke_status);

  // 获取输出张量的指针，并确保它具有我们期望的属性。它应该与输入张量相同。
  TfLiteTensor* output = interpreter.output(0);
  TF_LITE_MICRO_EXPECT_EQ(2, output->dims->size);
  TF_LITE_MICRO_EXPECT_EQ(1, output->dims->data[0]);
  TF_LITE_MICRO_EXPECT_EQ(1, output->dims->data[1]);
  TF_LITE_MICRO_EXPECT_EQ(kTfLiteInt8, output->type);

  // 获取输出张量的量化参数
  float output_scale = output->params.scale;
  int output_zero_point = output->params.zero_point;

  // 从模型的输出张量中获取量化的输出
  int8_t y_pred_quantized = output->data.int8[0];
  // 将输出从整数量化到浮点数
  float y_pred = (y_pred_quantized - output_zero_point) * output_scale;

  // 检查输出是否在预期输出的一个小范围内
  float epsilon = 0.05f;
  TF_LITE_MICRO_EXPECT_NEAR(y_true, y_pred, epsilon);

  // 对多个值进行推理，并确认预期的输出
  x = 1.f;
  y_true = sin(x);
  input->data.int8[0] = x / input_scale + input_zero_point;
  interpreter.Invoke();
  y_pred = (output->data.int8[0] - output_zero_point) * output_scale;
  TF_LITE_MICRO_EXPECT_NEAR(y_true, y_pred, epsilon);

  x = 3.f;
  y_true = sin(x);
  input->data.int8[0] = x / input_scale + input_zero_point;
  interpreter.Invoke();
  y_pred = (output->data.int8[0] - output_zero_point) * output_scale;
  TF_LITE_MICRO_EXPECT_NEAR(y_true, y_pred, epsilon);

  x = 5.f;
  y_true = sin(x);
  input->data.int8[0] = x / input_scale + input_zero_point;
  interpreter.Invoke();
  y_pred = (output->data.int8[0] - output_zero_point) * output_scale;
  TF_LITE_MICRO_EXPECT_NEAR(y_true, y_pred, epsilon);
}

// 结束单元测试的定义
TF_LITE_MICRO_TESTS_END